import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import * as _ from 'lodash';
import { ConfirmationService, MenuItem } from 'primeng/api';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { RDataConfig, RPaginatorArgv, RTable, RTablePaginator } from '../r-models/r-table.model';

@Injectable({
  providedIn: 'root'
})

/**
 * 公司：xxx有限公司
 * 版权：xxx2020
 * 描述：公共表格的常用方法
 * 作者：oyjl
 * 时间：2020-03-06 13:41:28
 */
export class RTableService {

  public paginatorArgv: any;  // 分页参数映射，key为前端表格分页参数，value为后端分页参数，主要用于不同的后端分页之间转换
  public msgs: Array<any>; // 消息提示
  public confirmConfig: any; // 确认框配置{header:xxx,icon: xxx,msg:xxx,accept:xxx}
  public cacheData: any; // 缓存初始表格数据
  public animationShow: boolean; // 等待动画
  public confirmVisible: boolean; // 弹框显示
  public confirmClosable: boolean; // 是否显示弹框关闭按钮
  public breadcrumbPath: MenuItem[] = []; // 树形结构面包屑路径
  constructor(
    public confirmationService: ConfirmationService,
    private http: HttpClient) {
    this.paginatorArgv = new RPaginatorArgv();
    this.msgs = [];
    this.confirmConfig = {};
    this.breadcrumbPath = [];
  }

  /**
   * 描述：分页参数处理，将后端的分页参数，处理成前端需要的格式
   * 作者：oyjl
   * 时间：2020-03-06 13:04:05
   */
  public paginatorSwitchToView(config, data): object {
    const paging: any = {};
    // tslint:disable-next-line:forin
    for (const key in config) {
      if (config.hasOwnProperty(key) && data.hasOwnProperty(config[key])) {
        paging[key] = data[config[key]];
      }
      if (key === 'pageNo') {
        paging[key] = (data[config[key]] - config.firstIndex + 1) || 1;
      }
    }
    return paging;
  }

  /**
   * 描述：分页参数处理，将前端的分页参数，处理成后端需要的格式
   * 作者：oyjl
   * 时间：2020-03-06 13:04:05
   */
  public paginatorSwitchToModel(config, data): object {
    const paging: any = {};
    for (const key in config) {
      if (config.hasOwnProperty(key) && data.hasOwnProperty(key)) {
        paging[config[key]] = data[key];
      }
    }
    return paging;
  }

  /**
   * 描述：设置分页参数
   * 作者：oyjl
   * 时间：2020-03-07 22:37:25
   */
  private paginatorSet(paginator: RTablePaginator, paging, paginatorArgv, status = true) {
    if (status) {
      paginator.first = 0;
      paginator.pageNo = 1;
    }
    paginator = Object.assign(paginator, this.paginatorSwitchToView(paginatorArgv, paging));
    paginator.pageCount = Math.ceil(paginator.total / paginator.pageSize);
  }

  /**
   * 描述：发送POST请求
   * 作者：oyjl
   * 时间：2020-03-07 23:34:14
   */
  public sendPost(rTable: RTable, data, url, paginatorArgv: RPaginatorArgv = this.paginatorArgv, status?): Observable<any> {
    this.animationShow = true;
    rTable.prop.value = [];
    const pageModelName = paginatorArgv.key;
    const pageInfo = this.paginatorSwitchToModel(paginatorArgv, rTable.paginator);
    pageInfo[paginatorArgv.pageNo] = pageInfo[paginatorArgv.pageNo] + paginatorArgv.firstIndex - 1;
    if (!_.isEqual(this.cacheData && this.cacheData.data, data)) {
      pageInfo[paginatorArgv.pageNo] = paginatorArgv.firstIndex;
    }
    let req = data || {};
    if (pageModelName) {
      req[pageModelName] = pageInfo;
    } else {
      req = Object.assign(req, pageInfo);
    }
    // 缓存查询参数，便于翻页
    this.cacheData = {
      data,
      url,
      paginatorArgv
    };
    return this.http.post(url, req).pipe(map((res: any = {}) => {
      if (rTable.prop.paginator) {
        let paging;
        if (pageModelName) {
          paging = res[pageModelName] || {};
        } else {
          paging = res || {};
        }
        this.paginatorSet(rTable.paginator, paging, paginatorArgv, status);
      }
      this.animationShow = false;
      const rows = this.afterDataSwitch(rTable.prop.dataConfig, res);
      rTable.prop.columns.forEach(element => {
        for (const item of rows) {
          if (element.initEvt) {
            element.initEvt(item, element);
          }
        }
      });
      const keys = rTable.prop.dataConfig.key.split('.');
      this.createObjectByArray(res, keys, rows);
      if (!(this.afterStatusSwitch(rTable.prop.dataConfig, res))) {
        this.message('error', res.msg);
      }
      return res;
    })
    );
  }
  /**
   * 描述：消息提示框，安全级别severity可取值：success、info、warn、error
   * 作者：oyjl
   * 时间：2020-03-07 13:20:05
   */
  public message(severity: string, summary: string) {
    this.msgs = []; // 置空消息数组，每次仅仅展示一条消息
    this.msgs.push({ severity, summary });
    const ti = setTimeout(() => {
      this.msgs = []; // 设置超时关闭
      clearTimeout(ti);
    }, 2000);
  }

  /**
   * 描述：表格独有确认信息弹框，有单独的key控制，仅仅在使用表格组件的时候有用
   * 作者：oyjl
   * 时间：2020-03-07 13:20:05
   * TODO:参数暂时未定义成接口
   */
  public confirm(config: any = {
    header: '', icon: '', msg: '', accept: null, closable: true,
    acceptVisible: true, rejectVisible: true, time: 0
  }) {
    this.confirmVisible = true;
    this.confirmClosable = config.closable === false ? false : true;
    this.confirmConfig = config;
    if (!this.confirmConfig.header) {
      this.confirmConfig.header = navigator.language === 'en' ? 'Message' : '消息';
    }
    this.confirmationService.confirm({
      key: 'tbConfirm',
      header: this.confirmConfig.header,
      message: this.confirmConfig.msg,
      accept: this.confirmConfig.accept,
      acceptVisible: this.confirmConfig.acceptVisible,
      rejectVisible: this.confirmConfig.rejectVisible,
      acceptLabel: this.confirmConfig.acceptLabel || '确定',
      rejectLabel: this.confirmConfig.rejectLabel || '取消'
    });
    if (config.time) {
      const time = setTimeout(() => {
        this.confirmVisible = false;
        clearTimeout(time);
      }, config.time);
    }
  }

  /**
   * 描述：日历中文初始化
   * 作者：oyjl
   * 时间：2020-03-06 17:53:05
   */
  public calendarZh(): object {
    const zh = {
      firstDayOfWeek: 0,
      dayNames: ['日', '一', '二', '三', '四', '五', '六'],
      dayNamesShort: ['日', '一', '二', '三', '四', '五', '六'],
      dayNamesMin: ['日', '一', '二', '三', '四', '五', '六'],
      monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      today: '今天',
      clear: '清除'
    };
    return zh;
  }

  /**
   * 描述：table表格中sw组件string转boolean(整个数据),仅仅对主表做转换
   * 作者：oyjl
   * 时间：2020-03-13 20:07:34
   */
  public stringToBoolean(data: Array<any>, columns: Array<any>): Array<any> {
    for (const item of data) {
      for (const element of columns) {
        if (element.type === 'sw') {
          item[element.field] = (item[element.field] - 0) ? true : false;
        }
      }
    }
    return data;
  }

  /**
   * 描述：table表格中sw组件boolean转boolean(单行数据),仅仅对主表做转换
   * 作者：oyjl
   * 时间：2020-03-13 20:07:34
   */
  public booleanToString(data: Array<any>, columns: Array<any>): Array<any> {
    for (const item of data) {
      for (const element of columns) {
        if (element.type === 'sw') {
          item[element.field] = item[element.field] ? '1' : '0';
        }
      }
    }
    return data;
  }

  /**
   * 描述：后端数据根据配置文件转换，例如{pageModel: { row: []}:},将row中的数据获取到并给表格组件value
   * 作者：oyjl
   * 时间：2020-04-22 09:07:51
   */
  public afterDataSwitch(dataConfig: RDataConfig, res): Array<any> {
    const keys = dataConfig.key.split('.');
    let data = _.cloneDeep(res);
    for (const i of keys) {
      if (data) {
        data = data[i];
      }
    }
    return data || [];
  }

  /**
   * 描述：后端状态处理
   * 作者：oyjl
   * 时间：2020-04-22 09:33:43
   */
  public afterStatusSwitch(dataConfig: RDataConfig, res): boolean {
    if (res[dataConfig.status] === dataConfig.value) {
      return true;
    }
    return false;
  }

  /**
   * 描述：获取token,先从cookie中获取，再从storage中获取
   * 作者：oyjl
   * 时间：2020-08-21 18:17:28
   */
  public getCookie(name) {
    let arr;
    const reg = new RegExp(name + '=([^;]*)');
    arr = document.cookie.match(reg) || sessionStorage.getItem(name);
    if (arr) {
      if (arr instanceof Array) {
        return unescape(arr[1]);
      } else {
        return arr;
      }
    } else {
      return null;
    }
  }
  /**
   * 描述：根据提供的数组，逐级创建对象，并将指定数据添加到对象里面
   * 作者：oyjl
   * 时间：2020-08-03 09:44:45
   * @param a：源对象；b：包含相应key值数组；key：保留上一次的key值；data：需要添加到指定对象的数据
   */
  public createObjectByArray(a, b: Array<string>, data, key?: string) {
    if (b instanceof Array && b.length > 0) {
      key = b[0];
      if (key && b.length === 1) {
        a[key] = data;
        return;
      }
      a[key] = {};
      b.shift();
      this.createObjectByArray(a[key], b, data, key);
    } else {
      console.error();
    }
  }
}
